# Ampliación de márgenes
from IPython.core.display import display, HTML, Image
display(HTML("<style>.container{ width:80% }</style>"))
import pandas as pd
import numpy as np
from matplotlib import pyplot
import matplotlib.pyplot as plt
plt.style.use('ggplot')
import seaborn as sns
import os, json, codecs, nltk , csv , re , glob
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer, TfidfTransformer
import string
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import KFold, cross_val_score
from sklearn import metrics
from sklearn.metrics import classification_report
from sklearn.metrics import precision_recall_fscore_support as score
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import GridSearchCV
from sklearn.naive_bayes import BernoulliNB
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix,accuracy_score,classification_report
from sklearn.metrics import recall_score,precision_score,f1_score
from sklearn.metrics import confusion_matrix
from sklearn.feature_selection import chi2
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
from sklearn.manifold import TSNE
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import cross_val_predict
from tqdm import tqdm
import bokeh
from bokeh.models import ColumnDataSource, HoverTool, LinearColorMapper, CustomJS, Slider, TapTool, TextInput
from bokeh.palettes import Category20, Accent, Pastel2, Pastel1, Set3
from bokeh.transform import linear_cmap, transform
from bokeh.io import output_file, show, output_notebook
from bokeh.plotting import figure
from bokeh.models import RadioButtonGroup, TextInput, Div, Paragraph
from bokeh.layouts import column, widgetbox, row, layout
from bokeh.layouts import column
import time
from IPython.display import HTML
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
La API de Twitter utiliza OAuth , un protocolo de autorización abierto ampliamente utilizado, para autenticar todas las solicitudes. Antes de realizar una llamada a la API de Twitter, se debe crear y configurar los credenciales de autenticación https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens
Estos tokens se deben guardar en un archivo json con la siguiente estructura:
{"ACCESS_KEY": "X", "ACCESS_SECRET": "X", "CONSUMER_KEY": "X", "CONSUMER_SECRET": "X" }
Se utiliza la libreria Tweepy para llevar a cabo el scrapping, ya que esta libreria ofrece una interfaz para acceder a la API de Twitter desde Python. Tweepy se encarga de todos los detalles para usar OAuth requerido por la API de Twitter para autenticar cada solicitud, proporciona una OAuthHandlerclase que puede usar para establecer las credenciales que se usarán en todas las llamadas a la API.
En este proyecto se van a extraer los tweets de un determinado usuario para una serie de tiempo, a través de user_timeline, para más información: https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-user_timeline
Para el análisis que se quiere llevar a cabo es conveniente recuperar tantos tweets como sea posible, pero twitter posee varias limitaciones, como es el nº máximo de tweets a descargar y el tiempo entre consulta y consulta.
Para solventar estar limitaciones, se lleva a cabo la siguiente estrategia. Hay que tener en cuenta que todo tweet posee un id numérico que sigue una serie temporal, lo que permite identificar si un tweet es más reciente que otro. Además, uno de los argumentos que se pueden indicar en la API es el parametro max_id que devuelve resultados con una ID menor (es decir, anterior a) o igual a la id especificada.
Por lo que se crea un contador al que se van añadiendo todos los tweets, a él se le añaden los nuevos tweets teniendo en cuenta que el parametro max_id es igual al último id almacenado, consiguiendo así que se extraigan de manera escalonada.
Esto proceso de automatiza en el archivo llamado Descarga_tweets.py. A continuación, simplemente se tiene que introducir el nombre del usuario de twitter, el proceso se hara iterativo creando un csv independiente para cada usuario que se quiera analizar.
usuario_twitter = 'Santi_ABASCAL'
%run Descarga_tweets.py $usuario_twitter
Tarda aproximadamente un minuto para cada csv
Pasos llevados a cabo:
%run Preprocesado_tweets.py
%matplotlib inline
plt.rcParams['figure.figsize']=(10,8)
%run Media_tweets_diarios.py
Como se puede apreciar Santiago Abascal es el más activo en Twitter, en concreto escribe o retwittea unos 12 tweets de media
test_name = 'PabloIglesias'
from gen_wordcloud import gen_wordcloud
gen_wordcloud(tweet_df[test_name])
from gen_wordcloud import gen_wordcloud
test_name = 'sanchezcastejon'
#plt.rcParams['figure.figsize']=(10,20)
gen_wordcloud(tweet_df[test_name])
from gen_wordcloud import gen_wordcloud
test_name = 'InesArrimadas'
gen_wordcloud(tweet_df[test_name])
from gen_wordcloud import gen_wordcloud
test_name = 'Santi_ABASCAL'
gen_wordcloud(tweet_df[test_name])
from gen_wordcloud import gen_wordcloud
test_name = 'pablocasado_'
gen_wordcloud(tweet_df[test_name])
A través de estos gráficos se pueden extraer infinidad de conclusiones, por ejemplo una interesante es que Ines Arrimadas, Santiago Abascal y Pablo Casado coinciden en que nombran la palabra sanchez, cuando se trata de tweets negativos
from engagement_plot import plot_engagement
test_name = 'InesArrimadas'
eng = plot_engagement(tweet_df, test_name)
eng.bubble_chart()
IFrame(src='./Pablo Iglesias.html', width=1500, height=800)
IFrame(src='./Ines Arrimadas.html', width=1500, height=800)
IFrame(src='./Pablo Casado.html', width=1500, height=800)
IFrame(src='./Pedro Sánchez.html', width=1500, height=800)
1. "Correlación entre pablocasado_ y InesArrimadas: 0.827740378209078"
2. "Correlación entre sanchezcastejon y PabloIglesias: 0.713946758364983"
3. "Correlación entre sanchezcastejon y InesArrimadas: 0.685166391057271"
4. "Correlación entre sanchezcastejon y pablocasado_: 0.681392922337161"
5. "Correlación entre InesArrimadas y PabloIglesias: 0.678833140792714"
6. "Correlación entre Santi_ABASCAL y pablocasado_: 0.653505576583905"
7. "Correlación entre pablocasado_ y PabloIglesias: 0.624692277853074"
8. "Correlación entre Santi_ABASCAL y InesArrimadas: 0.620161618329841"
9. "Correlación entre Santi_ABASCAL y sanchezcastejon: 0.524441448183372"
10."Correlación entre Santi_ABASCAL y PabloIglesias: 0.519256295109918"
Se observa como los 2 presidentes que mayor correlación poseen son Pablo Casado e Ines Arrimadas y por el contrario los 2 presidentes menos correlacionados son Santiago Abascal y Pablo Iglesias
IFrame(src='./Correlación entre usuarios.html', width=1500, height=800)

La idea de este trabajo es demostrar como una máquina también puede averiguar quién ha escrito un tweet en base a los patrones de sus tweets anteriores, es obvio que a un humano si nos dan un tweet de Santiago Abascal y otro de Pablo Iglesias simplemente por el contexto sabríamos decir quién lo ha escrito con un % de acierto del 100%, pero y sí desaparecen las menciones y los hanstag y queremos adivinar si el tweet lo ha escrito Pedro Sánchez y Pablo Iglesias, tal vez en esos casos ya un humano no acertaría al 100%. Por todo ello he creado varios prototipos de modelos de clasificación de autoría para analizar como de eficientes podrían llegar a ser estos.
politicos = pd.read_excel("Politicos_original.xlsx")
politicos = politicos[['User Twitter Handle', 'Tweet']]
politicos.columns = ['usuario', 'tweet']
pd.set_option('display.max_colwidth', 300)
Santiago_Abascal = politicos[(politicos.usuario=='Santi_ABASCAL')]
Pablo_Iglesias = politicos[(politicos.usuario=='PabloIglesias')]
extremos = pd.concat([Santiago_Abascal, Pablo_Iglesias], axis=0, join='inner',sort=False)
print("Hay un total de {} observaciones, {} son de Santiago Abascal y {} son de Pablo Iglesias, por lo que los datos están bien balanceados".format
(len(extremos),
len(extremos[extremos['usuario']=='Santi_ABASCAL']),
len(extremos[extremos['usuario']=='PabloIglesias']) ))
Se crean 2 variables adicionales:
import string
def contador_signos(text):
count = sum([1 for char in text if char in string.punctuation])
return round(count/(len(text) - text.count(" ")), 3)*100
extremos['tweet_longitud'] = extremos['tweet'].apply(lambda x: len(x) - x.count(" "))
extremos['n_signos'] = extremos['tweet'].apply(lambda x: contador_signos(x))
extremos
bins = np.linspace(0, 280, 100)
pyplot.hist(extremos[extremos['usuario']=='Santi_ABASCAL']['tweet_longitud'], bins, alpha=0.5,label='Santi_ABASCAL', color = "green")
pyplot.hist(extremos[extremos['usuario']=='PabloIglesias']['tweet_longitud'], bins, alpha=0.5,label='PabloIglesias', color = "purple")
pyplot.legend(loc='upper left', fontsize= 10)
pyplot.xticks(fontsize= 10)
pyplot.yticks(fontsize= 10)
pyplot.show()
pyplot.rcParams['figure.figsize']=(15,10)
bins = np.linspace(0, 18, 100)
pyplot.hist(extremos[extremos['usuario']=='Santi_ABASCAL']['n_signos'], bins, alpha=0.5, label='Santi_ABASCAL', color = "green")
pyplot.hist(extremos[extremos['usuario']=='PabloIglesias']['n_signos'], bins, alpha=0.5, label='PabloIglesias', color = "purple")
pyplot.legend(loc='upper left', fontsize= 10)
pyplot.xticks(fontsize= 10)
pyplot.yticks(fontsize= 10)
pyplot.show()
pyplot.rcParams['figure.figsize']=(15,10)
stopwords = nltk.corpus.stopwords.words('spanish')
stopwords = (stopwords)
def limpiar_texto(texto):
tokens = re.split('\W+', texto)
texto = [i for i in tokens if i not in stopwords]
extremos['tweet'] = extremos['tweet'].str.lower()
extremos['tweet'] = extremos['tweet'].str.replace("https?://[A-Za-z0-9./]+","")
extremos['tweet'] = extremos['tweet'].str.replace("http?://[A-Za-z0-9./]+","")
extremos['tweet'] = extremos['tweet'].str.replace("[\.\,\!\¡\¿\?\:\;\-\=\"\'\$\%\&\()\*\+\<\>\[\\]\[\]\^\_\´\{\}\|\~\'#\(\)]", "")
extremos['tweet'] = extremos['tweet'].str.replace("\d+", "") # nº
extremos['tweet'] = extremos['tweet'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>2]))
extremos['tweet'] = extremos['tweet'].apply(lambda x : ' '.join([tweet for tweet in x.split()if not tweet.startswith("@")]))
return texto
X_train, X_test, y_train, y_test = train_test_split(extremos[['tweet', 'tweet_longitud', 'n_signos']], extremos['usuario'], test_size=0.2)
len(X_train), len(X_test)
Se decide utilizar el método TF-IDF por ser el más conocido, pero se podría haber utilizado otro como N-grams o Count Ventorization entre otros. Básicamente consiste en que cuanto más raro es el termino, mayor valor tendrá en la ponderación.
Explicado a grandes rasgos, básicamente lo que se hara para entrenar el modelo es utilizar las palabras que son muy frecuentes en uno de ellos, pero que no lo son en absoluto en el otro y viceversa.
¿Quien escribió ese tweet? Para un humano tal vez es muy sencillo identificar si un mensaje es de extrema izquierda o extrema derecha por el contexto del tweet, pero ¿y para una máquina?
tfidf_vect = TfidfVectorizer(analyzer=limpiar_texto)
X_tfidf = tfidf_vect.fit_transform(extremos['tweet'])
X_tfidf_feat = pd.DataFrame(X_tfidf.toarray())
count_vect = CountVectorizer(analyzer=limpiar_texto)
X_count = count_vect.fit_transform(extremos['tweet'])
X_count_feat = pd.DataFrame(X_count.toarray())
rf = RandomForestClassifier()
param = {'n_estimators': [10, 150, 300],
'max_depth': [30, 60, 90, None]}
gs = GridSearchCV(rf, param, cv=5, n_jobs=-1)
gs_fit = gs.fit(X_tfidf_feat, extremos['usuario'])
pd.DataFrame(gs_fit.cv_results_).sort_values('mean_test_score', ascending=False)[0:5]
El mejor mean_test_score es 0.80
rf = RandomForestClassifier()
param = {'n_estimators': [10, 150, 300],
'max_depth': [30, 60, 90, None]}
gs = GridSearchCV(rf, param, cv=5, n_jobs=-1)
gs_fit = gs.fit(X_count_feat, extremos['usuario'])
pd.DataFrame(gs_fit.cv_results_).sort_values('mean_test_score', ascending=False)[0:5]
El mejor mean_test_score es 0.80, es decir el resulado es muy similar al proporcionado por tf-idf
tfidf_vect = TfidfVectorizer(analyzer=limpiar_texto)
tfidf_vect_fit = tfidf_vect.fit(X_train['tweet'])
tfidf_train = tfidf_vect_fit.transform(X_train['tweet'])
tfidf_test = tfidf_vect_fit.transform(X_test['tweet'])
X_train_vect = pd.concat([X_train[['tweet_longitud', 'n_signos']].reset_index(drop=True),
pd.DataFrame(tfidf_train.toarray())], axis=1)
X_test_vect = pd.concat([X_test[['tweet_longitud', 'n_signos']].reset_index(drop=True),
pd.DataFrame(tfidf_test.toarray())], axis=1)
X_train_vect.head()
rf = RandomForestClassifier(n_estimators=300, max_depth=60, n_jobs=-1)
start = time.time()
rf_model = rf.fit(X_train_vect, y_train)
end = time.time()
fit_time = (end - start)
start = time.time()
y_pred = rf_model.predict(X_test_vect)
end = time.time()
pred_time = (end - start)
precision, recall, fscore, train_support = score(y_test, y_pred, pos_label='Santi_ABASCAL', average='binary')
print('Fit time: {} / Predict time: {} ---- Precision: {} / Recall: {} / Accuracy: {}'.format(
round(fit_time, 3), round(pred_time, 3), round(precision, 3), round(recall, 3), round((y_pred==y_test).sum()/len(y_pred), 3)))
print(classification_report(y_test, y_pred))
cm = confusion_matrix(y_test, y_pred)
cmn = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
plt.figure(figsize = (8,5))
sns.heatmap(cmn,annot = True, xticklabels= ["Santi_ABASCAL","PabloIgleas"] ,
yticklabels= ["Santi_ABASCAL","PabloIgleas"], cmap="PiYG",linecolor = "grey",
fmt = ".05f", linewidths = 1, annot_kws={"size": 16})
plt.title("Confusion matrix", color="purple")
plt.xlabel('Predicted', color="purple")
plt.ylabel('Actual', color="green")
sns.set(font_scale=5.4)
plt.show()
En este se pretende averiguar cual de los 6 escribió el tweet
politicos = pd.read_excel("Politicoscontweetslimpios.xlsx")
politicos['id_usuario']=politicos['usuario'].factorize()[0]
pd.set_option('display.max_colwidth', 300)
category_id_df = politicos[['usuario', 'id_usuario']].drop_duplicates().sort_values ('id_usuario')
category_to_id = dict(category_id_df .values)
id_to_category = dict(category_id_df[['id_usuario','usuario']].values)
stopwords = nltk.corpus.stopwords.words('spanish')
tfidf = TfidfVectorizer(sublinear_tf=True, min_df=5, norm='l2', encoding='latin-1', ngram_range=(1, 2), stop_words=stopwords)
features = tfidf.fit_transform(politicos.Tweet).toarray()
labels = politicos.id_usuario
N = 2
for usuario, id_usuario in sorted(category_to_id.items()):
features_chi2 = chi2(features, labels == id_usuario)
indices = np.argsort(features_chi2[0])
feature_names = np.array(tfidf.get_feature_names())[indices]
unigrams = [v for v in feature_names if len(v.split(' ')) == 1]
bigrams = [v for v in feature_names if len(v.split(' ')) == 2]
print("________{}________:".format(usuario))
print("Unigramas más correlacionados:\n. {}".format('\n. '.join(unigrams[-N:])))
print("Bigramas más correlacionados:\n. {}".format('\n. '.join(bigrams[-N:])))
X_train, X_test, y_train, y_test = train_test_split(politicos['Tweet'], politicos['usuario'], test_size=0.2 , random_state = 22)
len(X_train), len(X_test)
vectorizer = CountVectorizer(stop_words=stopwords)
parameters = {
'vect__ngram_range': [(1, 1), (1, 2), (2, 2)],
'tfidf__use_idf': (True, False),
'tfidf__norm': ('l1', 'l2'),
'clf__alpha': [1, 0.1, 0.01, 0.001, 0.0001, 0.00001],
'tfidf__sublinear_tf': (True, False),
}
#Pipeline
pipeline = Pipeline([
('vect', vectorizer),
('tfidf', TfidfTransformer()),
('clf', MultinomialNB()),
])
grid_search = GridSearchCV(pipeline, parameters, n_jobs=-1, scoring= "accuracy")
grid_search.fit(X_train, y_train)
print(classification_report(y_test, grid_search.predict(X_test)))
y_pred = grid_search.predict(X_test)
cm = confusion_matrix(y_test, y_pred)
cmn = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
plt.figure(figsize = (8,5))
sns.heatmap(cmn,annot = True, xticklabels= ["Santi_ABASCAL","PabloIgleas", "InesArrimadas", "pablocasado_", "sanchezcastejon"] ,
yticklabels= ["Santi_ABASCAL","PabloIgleas", "InesArrimadas", "pablocasado_", "sanchezcastejon"], cmap="plasma",linecolor = "grey",
fmt = ".05f", linewidths = 1, annot_kws={"size": 16})
plt.title("Confusion matrix", color="purple")
plt.xlabel('Predicted', color="purple")
plt.ylabel('Actual', color="orange")
sns.set(font_scale=5.4)
plt.show()
Sánchez tiene la peor gestión económica, que el Banco de España agrava con un 15% de caída PIB y el 25% de paro. Según la OCDE nuestra economía será la más afectada de la zona euro y Eurostat sostiene que nuestro país lidera la destrucción de empleo. ¿Esto es salir más fuertes? pic.twitter.com/iYcT2MYlgW
— Pablo Casado Blanco (@pablocasado_) June 10, 2020
grid_search.predict(["Sánchez tiene la peor gestión económica, que el Banco de España agrava con un 15% de caída PIB y el 25% de paro. Según la OCDE nuestra economía será la más afectada de la zona euro y Eurostat sostiene que nuestro país lidera la destrucción de empleo. ¿Esto es salir más fuertes?"])
Hoy defendemos en el Congreso el #IngresoVital, que ayudará a llenar la nevera a millones de compatriotas y que es clave para una reconstrucción económica justa y eficaz. Hasta el Fondo Monetario Internacional aplaude la medida. Mientras, la derecha española le llama "paguita". pic.twitter.com/XEpxQQpW9M
— Pablo Iglesias 🔻 (@PabloIglesias) June 10, 2020
grid_search.predict(["Hoy defendemos en el Congreso el #IngresoVital, que ayudará a llenar la nevera a millones de compatriotas y que es clave para una reconstrucción económica justa y eficaz. Hasta el Fondo Monetario Internacional aplaude la medida. Mientras, la derecha española le llama paguita."])
Como se puede apreciar este modelo no posee unas métricas muy buenas, por lo que por ejemplo es incapaz de reconcoer el tweet de Pablo Iglesias y lo confunde con Pedro Sanchez.
Se ha llevado a cabo este prototipo de manera aproximada, para mejorarlo se podrían llevar a cabo técnicas como Feature Engineering (como se hacia en el anterior), también técnicas de Stemming o Lemmatizing y lo más importante de todo utilizar una base de datos mucho más amplia y utilizar modelos de Redes Neuronales
De igual manera que con la clasificación binaria se han probado más modelos y se dejan unicamente los mejores
pd.set_option('display.max_colwidth', 500)
Pedro_Sanchez = pd.read_excel("Politicos_tweets_limpios.xlsx")
Pedro_Sanchez = Pedro_Sanchez[(Pedro_Sanchez.usuario=='sanchezcastejon')]
Pedro_Sanchez = Pedro_Sanchez[['Tweet']]
vectorizer = CountVectorizer(max_df=0.9, min_df=25, token_pattern='\w+|\$[\d\.]+|\S+')
tf = vectorizer.fit_transform(Pedro_Sanchez['Tweet']).toarray()
tf_feature_names = vectorizer.get_feature_names()
number_of_topics = 10
model = LatentDirichletAllocation(n_components=number_of_topics, random_state=0)
model.fit(tf)
def display_topics(model, feature_names, no_top_words):
topic_dict = {}
for topic_idx, topic in enumerate(model.components_):
topic_dict["Topic nº%d palabras" % (topic_idx)]= ['{}'.format(feature_names[i])
for i in topic.argsort()[:-no_top_words - 1:-1]]
topic_dict["Topic nº%d pesos" % (topic_idx)]= ['{:.1f}'.format(topic[i])
for i in topic.argsort()[:-no_top_words - 1:-1]]
return pd.DataFrame(topic_dict)
no_top_words = 10
display_topics(model, tf_feature_names, no_top_words)
politicos = pd.read_excel("Tweetsdesdeoctubre.xlsx")
politicos.dropna(inplace=True)
def vectorizar(texto):
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(texto)
return X
text = politicos['Tweet_limpio'].values
X = vectorizar(text, )
X.shape
pca = PCA(n_components=0.95, random_state=55)
X_reduced= pca.fit_transform(X.toarray())
X_reduced.shape
distortions = []
K = range(2, 30)
for k in K:
k_means = KMeans(n_clusters=k, random_state=42, n_jobs=-1).fit(X_reduced)
k_means.fit(X_reduced)
distortions.append(sum(np.min(cdist(X_reduced, k_means.cluster_centers_, 'euclidean'), axis=1)) / X.shape[0])
X_line = [K[0], K[-1]]
Y_line = [distortions[0], distortions[-1]]
plt.plot(K, distortions, 'b-')
plt.plot(X_line, Y_line, 'r')
plt.xlabel('k')
plt.ylabel('Distortion')
plt.title('The Elbow Method showing the optimal k')
plt.show()
k = 13
kmeans = KMeans(n_clusters=k, random_state=42, n_jobs=-1)
y_pred = kmeans.fit_predict(X_reduced)
politicos['y'] = y_pred
tsne = TSNE(verbose=1, perplexity=100, random_state=42)
X_embedded = tsne.fit_transform(X.toarray())
%matplotlib inline
sns.set(rc={'figure.figsize':(15, 15)})
palette = sns.hls_palette(13, l=.4, s=.9)
sns.scatterplot(X_embedded[:,0], X_embedded[:,1], hue=y_pred, legend='full', palette=palette)
plt.title('Clustering')
plt.savefig("prueba2.png")
plt.show()
vectorizers = []
for i in range(0, 13):
vectorizers.append(CountVectorizer())
vectorized_data = []
for current_cluster, cvec in enumerate(vectorizers):
try:
vectorized_data.append(cvec.fit_transform(politicos.loc[politicos['y'] == current_cluster, 'Tweet_limpio']))
except Exception as e:
print("Error, no hay suficientes instancias en el cluster: " + str(current_cluster))
vectorized_data.append(None)
NUM_TOPICS_PER_CLUSTER = 10
lda_models = []
for i in range(0, 13):
lda = LatentDirichletAllocation(n_components=NUM_TOPICS_PER_CLUSTER, max_iter=10, learning_method='online',verbose=False, random_state=42)
lda_models.append(lda)
clusters_lda_data = []
for current_cluster, lda in enumerate(lda_models):
if vectorized_data[current_cluster] != None:
clusters_lda_data.append((lda.fit_transform(vectorized_data[current_cluster])))
def selected_topics(model, vectorizer, top_n=3):
current_words = []
keywords = []
for idx, topic in enumerate(model.components_):
words = [(vectorizer.get_feature_names()[i], topic[i]) for i in topic.argsort()[:-top_n - 1:-1]]
for word in words:
if word[0] not in current_words:
keywords.append(word)
current_words.append(word[0])
keywords.sort(key = lambda x: x[1])
keywords.reverse()
return_values = []
for i in keywords:
return_values.append(i[0])
return return_values
all_keywords = []
for current_vectorizer, lda in enumerate(lda_models):
if vectorized_data[current_vectorizer] != None:
all_keywords.append(selected_topics(lda, vectorizers[current_vectorizer]))
all_keywords
def classification_report(model_name, test, pred):
from sklearn.metrics import precision_score, recall_score
from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
print(model_name, ":\n")
print("Accuracy Score: ", '{:,.3f}'.format(float(accuracy_score(test, pred)) * 100), "%")
print(" Precision: ", '{:,.3f}'.format(float(precision_score(test, pred, average='macro')) * 100), "%")
print(" Recall: ", '{:,.3f}'.format(float(recall_score(test, pred, average='macro')) * 100), "%")
print(" F1 score: ", '{:,.3f}'.format(float(f1_score(test, pred, average='macro')) * 100), "%")
X_train, X_test, y_train, y_test = train_test_split(X.toarray(),y_pred, test_size=0.2, random_state=42)
sgd_clf = SGDClassifier(max_iter=10000, tol=1e-3, random_state=55, n_jobs=-1)
sgd_clf.fit(X_train, y_train)
sgd_pred = cross_val_predict(sgd_clf, X_train, y_train, cv=3, n_jobs=-1)
classification_report("Stochastic Gradient Descent Report (Training Set)", y_train, sgd_pred)
sgd_pred = cross_val_predict(sgd_clf, X_test, y_test, cv=3, n_jobs=-1)
classification_report("Stochastic Gradient Descent Report (Testing Set)", y_test, sgd_pred)
sgd_cv_score = cross_val_score(sgd_clf, X.toarray(), y_pred, cv=10)
print("Mean cv Score - SGD: {:,.3f}".format(float(sgd_cv_score.mean()) * 100), "%")
from configuracion.plot_text import header
output_notebook()
y_labels = y_pred
source = ColumnDataSource(data=dict(
x= X_embedded[:,0],
y= X_embedded[:,1],
x_backup = X_embedded[:,0],
y_backup = X_embedded[:,1],
desc= y_labels,
usuario= politicos['usuario'],
Tweet = politicos['Tweet'],
labels = ["Cluster nº. " + str(x) for x in y_labels],
))
hover = HoverTool(tooltips=[("Usuario", "@usuario{safe}"),("Tweet", "@Tweet{safe}"),], point_policy="follow_mouse")
initial_palette = Set3[12] #Accent, Pastel2, Pastel1, Set3
#random.Random(5).shuffle(initial_palette)
mapper = linear_cmap(field_name='desc',
palette=Set3[12],
low=min(y_labels) ,high=max(y_labels))
# prepare the figure
plot = figure(plot_width=1000, plot_height=650,
tools=[hover, 'pan', 'wheel_zoom', 'box_zoom', 'reset', 'save', 'tap'],
title="Clustering tweets presidentes politicos",
toolbar_location="above")
plot.scatter('x', 'y', size=5,
source=source,
fill_color=mapper,
line_alpha=0.3,
line_width=1.1,
line_color="steelblue",
legend = 'labels')
plot.legend.background_fill_alpha = 0.6
plot = layout([[plot]])
plot.sizing_mode = "scale_both"
output_file('t-sne_covid-19_interactive.html')
show(plot)
IFrame(src='./Clustering_tweets.html', width=1500, height=800)